# -*- encoding: utf-8 -*-
#!/usr/bin/python



import threading, Queue,thread
import logging
import traceback
import time
import signal

class WorkerThreadTemplate(threading.Thread):
    '''
    Stoppable woker Template thread.
    Supported: config reload, exitting
    '''
    WAIT_QUEUE_TIMEOUT=3
    def __init__(self, name, config, q):
        # call parent class init
        super(WorkerThreadTemplate, self).__init__(name=name)
        self.daemon = True

        """ constructor, setting initial variables """
        # save config items
        self._conf=config.copy()
        self._new_conf=config.copy()
        self._q = q

        # thread lock
        self._task_lock=thread.allocate_lock()

        # create an event for exiting gracefull and reload config dynamically
        self._stop_event=threading.Event()
        self._reload_event=threading.Event()

    def exitFlag(self):
        return self._stop_event.isSet()

    def reloadFlag(self):
        return self._reload_event.isSet()

    def doTask(self, task):
        '''
        Real sub works must implement this main task doing function
        :param task:
        :return:
        '''
        raise NotImplementedError

    # do something before leaving
    def goodbye(self) :
        pass

    # thread main function
    def run(self) :
        """ thread entry function
        Initialize the thread
        Wait for tasks from the self._q, and invoke the doTask()
        """
        logging.info("%s starts" % self.getName())

        try:
            # Main loop of the thread
            while not self.exitFlag() :
                get_task = False
                try :
                    # get task from queue with timeout
                    task=self._q.get(timeout=WorkerThreadTemplate.WAIT_QUEUE_TIMEOUT)
                    get_task = True
                except Queue.Empty:
                    # timeout
                    pass
                # reload config if necessary
                if self.reloadFlag() :
                    self.reloadConfig()

                # do task
                if get_task :
                    logging.info("%s get task %s" % (self.getName(), task))
                    self.doTask(task)

            # exit from the main loop
            self.goodbye()
        except:
            logging.error(traceback.format_exc(), self.getName())
        logging.info("%s ends" % self.getName())


    def addTask(self, task):
        """ add a task
        Put a task in the private queue
        """
        logging.debug("%s add task: %s" % (self.getName(), task))
        try :
            self._q.put(task)
        except :
            logging.error("%s fail to add task [%s]" % (self.getName(), task, traceback.format_exc()))

    def terminate(self):
        """ Stop the thread
        Send a 'exit' task to the private Queue
        """
        self._stop_event.set()

    def notifyReloadConfig(self, conf):
        """ notify thread to reload config interface
        copy the new config to _new_conf, and set the reload flag for copy to really conf place
        """
        try :
            self._task_lock.acquire()
            self._new_conf = conf.copy()
            logging.info("Notify %s to reload config" % self.getName())
        finally :
            self._task_lock.release()
        self._reload_event.set()

    def reloadConfig(self):
        '''
        Reload conf to realy place from the temp self._new_conf
        :return:
        '''
        try :
            self._task_lock.acquire()
            self._conf = self._new_conf.copy()
            logging.info("%s is reloading config" % self.getName())
        finally :
            self._task_lock.release()
        # reset reload event
        self._reload_event.clear()

    def start(self):
        super(WorkerThreadTemplate, self).start()


'''
    Global Variables
'''
g_stop_now_flag = False
g_reload_config_flag = False

'''
    Signal Handlers
'''
def sig_handler_start_pdb(signal, trace):
    import pdb
    pdb.set_trace()

def sig_handler_term(signum, frame):
    logging.info('SIGTERM received')
    global g_stop_now_flag
    g_stop_now_flag = True

def sig_handler_int(signum, frame):
    logging.info('SIGINT received')
    global g_stop_now_flag
    g_stop_now_flag = True

def sig_handler_hup(signum, frame):
    logging.info('SIGHUP received')
    global g_reload_config_flag
    g_reload_config_flag = True

#-----Sample code---------------
class SampleWokerThread(WorkerThreadTemplate):
    def __init__(self,name, config,q):
        super(SampleWokerThread, self).__init__(name, config,q)
    def doTask(self, task):
        print 'Doing task: ', task
        time.sleep(2)
    def goodbye(self):
        print 'I could do sth. before exiting'

class SampleWokerThreadPool():
    def __init__(self,conf,worker_num=10):
        self._q=Queue.Queue()

        # construct workers
        self._workers = []
        for i in range(worker_num):
            name='SampleThread-%d'%i
            conf['name']= name
            t = SampleWokerThread(name,conf,self._q)
            self._workers.append(t)

    def start(self):
        for t in self._workers:
            t.start()

    def addTask(self,task):
        logging.debug('Add task to queue {}'.format(task))
        self._q.put(task)

    def terminate(self) :
        for t in self._workers :
            t.terminate()
        for t in self._workers :
            t.join()

    def notifyReloadConfig(self, conf) :
        for t in self._workers :
            t.notifyReloadConfig(conf)

    def qsize(self) :
        return self._q.qsize()

if __name__ == '__main__':
    import sys,os
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
    FEED_THREAD_INTERVAL = 3

    signal.signal(signal.SIGTERM, sig_handler_term)
    signal.signal(signal.SIGINT, sig_handler_int)
    signal.signal(signal.SIGHUP, sig_handler_hup)
    signal.signal(signal.SIGQUIT, sig_handler_start_pdb)

    conf={}
    conf['dir']='dirtest'

    works = SampleWokerThreadPool(conf,worker_num=4)
    works.start()
    while not g_stop_now_flag:
        # global g_reload_config_flag

        print 'MainPID',os.getpid()
        works.addTask('task1')
        works.addTask('task2')
        print 'task Number',works.qsize()
        conf['dir']='reload test'

        if g_reload_config_flag:
            g_reload_config_flag = False
            works.notifyReloadConfig(conf)

        works.addTask((1,2,3,4))
        time.sleep(FEED_THREAD_INTERVAL)
    works.terminate()
    print 'task Number',works.qsize()
